#include <string.h>

#include "wave_codec.h"
#include "nonos.h"
#include "share/adpcm_ima.h"

#define LOGTAG "WAVE_CODEC"

const trunk_t* wave_codec_get_next_trunk(const void *fileData, uint32_t fileLen, const trunk_t *trunk) {
	if(!fileData || !fileLen) { return NULL; }
	
	if(!trunk) {
		// 第一个 trunk 就是文件的起始位置
		trunk = fileData;
	} else if(trunk == fileData) {
		// 上一个 trunk 是 RIFF 块，其大小是固定的，trunkSize表示文件大小，不能用 trunkSize 来计算下一个 trunk 偏移位置
		trunk = (trunk_t*)((uint32_t)trunk + 12);
	} else {
		// 根据上一个 trunk 的 trunkSize 计算下一个 trunk 的偏移地址
		trunk = (trunk_t*)(((uint32_t)trunk) + 8 + le2h32(trunk->trunkSize));
	}
	
	// 合法性校验
	uint32_t fileEndAddr = (uint32_t)fileData + fileLen;
	// 剩余的空间不足以存储一条 trunk 记录(每个 trunk 最小为 12 字节 = 8 字节头 + 最少 4 字节数据)
	if(((uint32_t)trunk) + 12 > fileEndAddr) { return NULL; } 
	// 剩余的空间不足以存储本 trunk 的所有数据
	if(((uint32_t)trunk) + 8 + le2h32(trunk->trunkSize) > fileEndAddr) { return NULL; } 
	
	return trunk;
}

std_err_t wave_codec_parse_file(const void *fileData, uint32_t fileLen, /*out*/wave_runtime_t *pinfo) {
	if(!fileData || !fileLen || !pinfo) { return STD_ERR_INVALID_ARG; }
	uint32_t fileSize = 0;
	
	const trunk_t *trunk = wave_codec_get_next_trunk(fileData, fileLen, NULL);
	while(trunk) {
		char id[5]; memcpy(id, trunk, 4); id[4] = '\0';
		//logd("'%s': %d bytes\r\n", id, trunk->trunkSize);
		
		if(trunk->ID == be2h32(TRUNK_ID_RIFF)) {
			// RIFF 块
			riff_trunk_t *riff = (riff_trunk_t *)trunk;
			//if(riff->WAVE != be2h32(TRUNK_ID_WAVE)) { return STD_ERR_INVALID_FORMAT; }
			fileSize = le2h32(riff->fileSize);
			// 判断文件大小是否正确
			if(fileSize + 8 > fileLen) { return STD_ERR_TOO_FEW_DATA; }
		}
		else if(trunk->ID == be2h32(TRUNK_ID_FMT)) {
			// FMT 块
			format_trunk_t *fmt = (format_trunk_t *)trunk;
			pinfo->audioFormat   	= le2h16(fmt->audioFormat);
			pinfo->nChannels     	= le2h16(fmt->nChannels);
			pinfo->sampleRate    	= le2h32(fmt->sampleRate);
			pinfo->bitsPerSample 	= le2h16(fmt->bitsPerSample);
			pinfo->blockAlign    	= le2h16(fmt->blockAlign);
			
			/*if(info.audioFormat == FMT_PCM) {
				// PCM 不分块，每个块就是一个采样点
				info.samplesPerBlock = 1;
			} else*/ if(pinfo->audioFormat == FMT_ADPCM_IMA) {
				// ADPCM-IMA 格式每个块中包含的采样点数量
				pinfo->samplesPerBlock = le2h16(fmt->ext.adpcm_ima.samplesPerBlock);
			} else {
				// 其它格式暂不支持 
				return STD_ERR_NOT_SUPPORTED;
			}
		}
		else if(trunk->ID == be2h32(TRUNK_ID_DATA)) {
			// DATA 块
			if(pinfo->audioFormat == FMT_PCM) {
				pinfo->sampleCount = le2h16(trunk->trunkSize) / (pinfo->bitsPerSample >> 3);
				pinfo->blockCount = le2h16(trunk->trunkSize) / pinfo->blockAlign;
			}
			else if(pinfo->audioFormat == FMT_ADPCM_IMA) {
				pinfo->blockCount = le2h16(trunk->trunkSize) / pinfo->blockAlign;
				pinfo->sampleCount = pinfo->blockCount * pinfo->samplesPerBlock;
			}
			// 第一个数据块
			pinfo->firstBlock = &trunk->data;
			pinfo->currentBlock = 0;
		}
		
		// 下一个 trunk
		trunk = wave_codec_get_next_trunk(fileData, fileLen, trunk);
	}
	
	return STD_ERR_OK;
}

std_err_t wave_codec_decode(wave_runtime_t *info) {
	std_err_t err = STD_ERR_OK;
	uint8_t *blockData;
	uint16_t sampleBufSize = info->samplesPerBlock * 2;
	s16 *outbuf;
	int nSampleCount;
	
	if(info->samplesQueue) {
		// 看采样点队列中是否有足够的空间存放一次解码出来的采样点数据，否则就继续等待队列中的数据被读走
		if(QChar_free(info->samplesQueue) < sampleBufSize) { return STD_ERR_CONTINUE; }
	}
	
	// 计算当前要解码的数据块的偏移地址
	if(info->currentBlock >= info->blockCount) { return STD_ERR_EOF; }
	blockData = ((uint8_t*)info->firstBlock) + (info->blockAlign * info->currentBlock);
	
	// 分配解码缓冲区
	outbuf = (s16*)osMalloc(sampleBufSize); // !!!
	if(!outbuf) { err = STD_ERR_NO_MEM; goto _final; }
	
	// 解码当前 block， 并指向下一个数据块
	nSampleCount = adpcm_decode_block(outbuf, blockData, info->blockAlign, info->nChannels); info->currentBlock++;
	if(nSampleCount == 0) { err = STD_ERR_FAIL; goto _final; }
	
	//for(int i=0; i<nSampleCount; i++) { 
	//	outbuf[i] = outbuf[i] * 2;
		//log("%6d ", outbuf[i]); 
	//}
	
	// 如果采样点队列未创建，则创建它
	if(!info->samplesQueue) {
		// 创建采样点队列，大小要能够存储 2 个 block 的采样点数量
		info->samplesQueue = QChar_create(sampleBufSize * 2); // !!!
		//print_memory_usage();
	}
	if(!info->samplesQueue) { err = STD_ERR_NO_MEM; goto _final; }
	
	// 把解码出来的采样点数据放入队列
	QChar_putn(info->samplesQueue, (uint8_t*)outbuf, nSampleCount*2);
	//logd("Decoded %d samples.\r\n", nSampleCount);

_final:
	// 释放解码缓冲区
	if(outbuf) { osFree(outbuf); }
	return err;
}
